package com.recharge.service.httpInterFace;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.recharge.common.result.ResultDO;
import com.recharge.common.result.ResultSupport;
import com.recharge.common.util.*;
import com.recharge.common.util.http.HttpClientUtil;
import com.recharge.common.util.http.MapTool;
import com.recharge.domain.query.OrderStatusQueryDO;
import com.recharge.domain.vo.GoodsChannelDO;
import com.recharge.domain.vo.ProviderApiDO;
import com.recharge.domain.vo.ProviderCallbackDO;
import com.recharge.service.ProviderApiService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

@Service
public class ReYiHTTPImpl implements ProviderHTTPService {

    private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

    @Autowired
    ProviderApiService providerApiService;


    @Override
    public ResultDO commitOrder(GoodsChannelDO domain) {
//        if (!verifyCommitParam(domain)) {
//            return returnFail(false
//                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
//                    , String.format("订单提交 >> 参数缺失 %s", domain.toString())
//                    , "请求参数缺失"
//                    , null);
//        }
        ProviderCallbackDO callbackDO = new ProviderCallbackDO();
        callbackDO.setSerialNumber(domain.getProviderSerialNumber());
        callbackDO.setProviderId(domain.getProviderId());
        callbackDO.setOrderNo(domain.getOrderNo());
        callbackDO.setStatus(Constant.CALL_BACK_PROCESS);
        ResultDO result = providerApiService.addCallback(callbackDO);
        if (!result.isSuccess()) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 订单%s添加存根信息失败", domain.getProviderSerialNumber())
                    , "添加回调存根信息失败"
                    , null);
        }
        //merchantId
        String merchantId = domain.getUserId();
        // 发送时间
        String timestamp = LocalDateUtils.getTodayByFormat(LocalDateUtils.DATE_FORMAT_SHORT6);
        //平台流水号/上游订单号
        String merOrderId = domain.getProviderSerialNumber();
        //充值号码
        String phoneNo = domain.getRechargeNumber();
        //请求金额
        Long txnAmt = domain.getAmount() / 10;
        //供应商密钥 app_secret
        String gateway = "YDQ";
        switch (domain.getServiceProvider()){
            case 1:
                gateway = "YDQ";
                break;
            case 2:
                gateway = "DXQ";
                break;
            default:
                gateway = "LTQ";
                break;
        }
        //回调地址
        String backUrl = domain.getCallback();
        //供应商密钥 app_secret
        String privateKey = domain.getPrivateKey();
        //签名
        String afterSign = "";
        TreeMap<String, String> requestParamMap = new TreeMap<>();
        try {
            requestParamMap.put("merchantId", merchantId);
            requestParamMap.put("sendTime", timestamp);
            requestParamMap.put("merOrderId", merOrderId);
            requestParamMap.put("txnAmt", String.valueOf(txnAmt));
            requestParamMap.put("backUrl", backUrl);
            requestParamMap.put("phoneNo", phoneNo);
            requestParamMap.put("gateway", gateway);
            String beforeSign = MapTool.join(requestParamMap) + privateKey;
            logger.info("before_sign: " + beforeSign);
            afterSign = Base64Util.encode(MD5Util.md5(beforeSign).getBytes());
        } catch (Exception e) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("渠道商ID: %s HTTP请求参数构建失败", domain.getProviderId())
                    , "HTTP请求参数构建失败"
                    , null);
        }
        requestParamMap.put("signature", afterSign);
        requestParamMap.put("signMethod", "MD5");
        String sendResult = HttpClientUtil.doPost(domain.getRequestUrl(), requestParamMap);
        logger.warn(String.format("订单提交 >> 请求返回信息: 供货流水%s -> %s", domain.getProviderSerialNumber(), sendResult));
        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSONObject.parseObject(sendResult);
        } catch (Exception e) {
            logger.info(String.format("订单提交 >> 请求返回结果解析json失败: %s", sendResult));
        }
        if (resultJson == null || resultJson.size() == 0) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 供货流水 %s -> 订单提交没有返回信息", domain.getProviderSerialNumber())
                    , "没有返回信息"
                    , sendResult);
        }
        String code = null;
        String message = "";
        try {
            code = resultJson.getString("code");
            message = resultJson.getString("msg");
        } catch (Exception e) {
            logger.info(String.format("获取返回信息失败: 供货流水%s -> %s", domain.getProviderSerialNumber(), sendResult));
        }
        if (code == null) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 供货流水 %s -> 没有获取到返回状态码", domain.getProviderSerialNumber())
                    , "订单提交没有返回状态码"
                    , sendResult);
        }
        // 状态码: 下单失败
        String[] array = {"0001", "0002", "0011", "0012", "2001", "2003", "0108", "0001", "2000"};
        //状态码判断
        if ("1111".equals(code)) { //成功
            JSONObject data = resultJson.getJSONObject("data");
            logger.info(String.format("订单提交 >> 订单 %s -> 订单提交成功|%s %s", domain.getProviderSerialNumber(), code, message));
            result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.BUSINESS_RECHARGE_RETURN_STATUS_SUCCESS);
            result.setModel(ResultDO.SECOND_MODEL_KEY, "");
            return result;
        } else if(Arrays.asList(array).contains(code)){
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 供货流水 %s -> 订单提交失败|%s %s", domain.getProviderSerialNumber(), code, message)
                    , String.format("%s %s", message, code)
                    , sendResult);
        } else if("9999".equals(code)){
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 供货流水 %s -> 订单提交未确认|%s %s", domain.getProviderSerialNumber(), code, TemplateUtils.isEmpty(message) ? "订单未确认" : message)
                    , String.format("%s %s", TemplateUtils.isEmpty(message) ? "订单未确认" : message, code)
                    , sendResult);
        } else {
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 供货流水 %s -> 订单提交未正确定义状态码|%s %s", domain.getProviderSerialNumber(), code, message)
                    , String.format("%s %s", message, code)
                    , sendResult);
        }
    }

    @Override
    public ResultDO backOrder(ProviderApiDO domain, String paramStr, String bodyStr) {
        ResultDO result = new ResultSupport();
        Map<String, String> paramMap = null;
        try {
            paramMap = JSONObject.parseObject(paramStr, Map.class);
            logger.info("订单异步回调 >> " + domain.getProviderNo() + "解析后数据: " + paramStr);
        } catch (Exception e) {
            logger.info("订单异步回调 >> " + domain.getProviderNo() + "解析json失败:" + paramStr);
        }
        if (paramMap == null) {
            logger.info("订单异步回调 >> 收到异步回调,但是没有回调信息");
            result.setSuccess(false);
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            return result;
        }
        Map<String, String> beforeParamMap = null;
        //签名
        String afterSign = "";
        try {
            //加密前参数
            beforeParamMap = paramMap.entrySet().stream().filter(item -> !"signature".equals(item.getKey()) && !"signMethod".equals(item.getKey()) && item.getValue() != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            Map<String, String> signParamMap = Maps.newLinkedHashMap();
            beforeParamMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(e -> signParamMap.put(e.getKey(), String.valueOf(e.getValue())));
            logger.info("加密前的数据: " + signParamMap);
            String beforeSign = MapTool.join(signParamMap) + domain.getPrivateKey();
            logger.info("before_sign: " + beforeSign);
            afterSign = Base64Util.encode(MD5Util.md5(beforeSign).getBytes());//DigestUtils.sha1Hex(beforeSign);
            logger.info("afterSign: " + afterSign);
        } catch (Exception e) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("签名异常", domain.getProviderId())
                    , "签名异常"
                    , null);
        }
        if(afterSign.equals(String.valueOf(paramMap.get("signature")))){
            String respCode = String.valueOf(paramMap.get("respCode"));
            String respMsg = String.valueOf(paramMap.get("respMsg"));
            String merOrderId = String.valueOf(paramMap.get("merOrderId"));
            String mobileOrderId = String.valueOf(paramMap.get("mobileOrderId"));
            if("1001".equals(respCode)){//成功
                logger.info(String.format("订单异步回调 >> 订单异步返回:%s 成功 status: %s", merOrderId, respCode));
                result = providerApiService.updateCallback(merOrderId, mobileOrderId, "", Constant.CALL_BACK_SUCCESS);
            } else if("1002".equals(respCode)){//失败
                logger.info(String.format("订单异步回调 >> 订单异步返回:%s 失败 status: %s", merOrderId, respCode + "|" + respMsg));
                result = providerApiService.updateCallback(merOrderId, "", "", Constant.CALL_BACK_FAIL);
            } else {//未确认
                logger.info(String.format("订单异步回调 >> 订单异步返回:%s 未确认 status: %s", merOrderId, respCode + "|" + respMsg));
                result = providerApiService.updateCallback(merOrderId, "", "", Constant.CALL_BACK_NUKNOW);
            }
            if (!result.isSuccess()) {
                logger.info("订单异步回调 >> 更新订单(" + merOrderId + ")状态失败");
                result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
                return result;
            }
            result.setModel(ResultDO.FIRST_MODEL_KEY, "success");
        } else {
            logger.info("订单异步回调 >> 签名校验: 加密前 >>" + beforeParamMap);
            logger.info(String.format("订单异步回调 >> 订单:%s 签名校验不一致 {本地签名:%s 返回签名:%s }", String.valueOf(paramMap.get("merOrderId")), afterSign, String.valueOf(paramMap.get("signature"))));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
        }
        return result;
    }

    @Override
    public ResultDO queryOrder(OrderStatusQueryDO domain) {
        ResultDO result = new ResultSupport();
        if (domain.getPrivateKey() == null || domain.getUserId() == null) {
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "查询失败");
            return result;
        }
        result.setSuccess(false);
        result.setModel(ResultDO.THIRD_MODEL_KEY, "接口API未实现");
        return result;
    }

    @Override
    public ResultDO queryBalance(ProviderApiDO domain) {
        ResultDO result = new ResultSupport();
        if (domain.getPrivateKey() == null || domain.getUserId() == null) {
            result.setSuccess(false);
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        // 发送时间
        String timestamp = LocalDateUtils.getTodayByFormat(LocalDateUtils.DATE_FORMAT_SHORT6);
        TreeMap<String, String> requestParamMap = new TreeMap<>();
        requestParamMap.put("merchantId", domain.getUserId());
        requestParamMap.put("timestamp", timestamp);
        //签名
        String afterSign = "";
        try {
            String beforeSign = MapTool.join(requestParamMap) + domain.getPrivateKey();
            logger.debug("before_sign: " + beforeSign);
            afterSign = Base64Util.encode(MD5Util.md5(beforeSign).getBytes());
        } catch (Exception e) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("渠道商ID: %s HTTP请求参数构建失败", domain.getProviderId())
                    , "HTTP请求参数构建失败"
                    , null);
        }
        requestParamMap.put("signature", afterSign);
        requestParamMap.put("signMethod", "MD5");

        logger.debug(String.format("余额查询 >> 余额查询请求参数: %s", requestParamMap));
        // 发送请求
        String sendResult = HttpClientUtil.doPost(domain.getBalanceFindUrl(), requestParamMap);
        logger.debug(String.format("余额查询 >> 余额查询返回信息: 供应商账号 %s -> %s", domain.getUserId(), sendResult));
        if(TemplateUtils.isEmpty(sendResult)){
            result.setSuccess(false);
            result.setModel(ResultDO.FIRST_MODEL_KEY, "HTTP请求失败");
            return result;
        }
        // 解析请求返回结果转换为json对象
        Map<String, String> paramMap = null;
        try {
            paramMap = JSONObject.parseObject(sendResult, Map.class);
            logger.debug("余额查询 >> " + domain.getProviderNo() + "解析后数据: " + sendResult);
        } catch (Exception e) {
            logger.debug("余额查询 >> " + domain.getProviderNo() + "解析json失败:" + sendResult);
            result.setSuccess(false);
            result.setModel(ResultDO.FIRST_MODEL_KEY, "解析json失败");
            return result;
        }

        Map<String, String> beforeParamMap = null;
        //签名
        String checkSign = "";
        try {
            //加密前参数
            beforeParamMap = paramMap.entrySet().stream().filter(item -> !"signature".equals(item.getKey()) && !"signMethod".equals(item.getKey()) && item.getValue() != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            Map<String, String> signParamMap = Maps.newLinkedHashMap();
            beforeParamMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(e -> signParamMap.put(e.getKey(), String.valueOf(e.getValue())));
            logger.debug("加密前的数据: " + signParamMap);
            String beforeSign = MapTool.join(signParamMap) + domain.getPrivateKey();
            logger.debug("before_sign: " + beforeSign);
            checkSign = Base64Util.encode(MD5Util.md5(beforeSign).getBytes());
            logger.debug("afterSign: " + checkSign);
        } catch (Exception e) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("签名异常", domain.getProviderId())
                    , "签名异常"
                    , null);
        }
        if (paramMap == null || paramMap.size() <=0) {
            result.setSuccess(false);
            logger.debug(String.format("余额查询 >> 供应商账号: %s -> 余额查询没有返回信息", domain.getUserId()));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        if(checkSign.equals(String.valueOf(paramMap.get("signature")))){
            System.out.println(paramMap);
            if(paramMap.get("balance") != null){
                Map<String, String> balanceMap = null;
                try {
                    balanceMap = JSONObject.parseObject(String.valueOf(paramMap.get("balance")), Map.class);
                    logger.debug("余额查询 >> " + domain.getProviderNo() + "解析后数据: " + balanceMap);
                    if(balanceMap != null){
                        result.setSuccess(true);
                        result.setModel(ResultDO.FIRST_MODEL_KEY, String.valueOf(ArithUtils.div(Double.valueOf(String.valueOf(balanceMap.get("availableBalance"))) , 100D)));
                    } else {
                        result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.debug("余额查询 >> " + domain.getProviderNo() + "解析json失败:" + sendResult);
                    result.setSuccess(false);
                    result.setModel(ResultDO.FIRST_MODEL_KEY, "解析json失败");
                }
            } else {
                result.setSuccess(false);
                logger.debug(String.format("余额查询 >> 供应商账号: %s -> 余额查询没有返回信息", domain.getUserId()));
                result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败 " + paramMap.get("msg"));
                return result;
            }
        } else {
            result.setSuccess(false);
            logger.debug(String.format("余额查询 >> 供应商账号: %s -> 余额查询没有返回信息", domain.getUserId()));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败, 签名错误");
            return result;
        }
        return result;
    }
    /**
     * 验证提交参数
     *
     * @param domain
     * @return boolean
     */
    private boolean verifyCommitParam(GoodsChannelDO domain) {
        // 充值号码
        if (TemplateUtils.isEmpty(domain.getRechargeNumber())) {
            return false;
        }
        // 面值
        if (TemplateUtils.isEmpty(domain.getAmount())) {
            return false;
        }
        // 流水号
        if (TemplateUtils.isEmpty(domain.getProviderSerialNumber())) {
            return false;
        }
        // 上游账号
        if (TemplateUtils.isEmpty(domain.getUserId())) {
            return false;
        }
        // 密钥
        if (TemplateUtils.isEmpty(domain.getPrivateKey())) {
            return false;
        }
        // 商户ID
        if (TemplateUtils.isEmpty(domain.getProviderId())) {
            return false;
        }
        return true;
    }

    /**
     * <h2>提交订单返回失败</h2>
     *
     * @param success    是否处理成功
     * @param code       订单状态
     * @param logMessage 失败信息
     * @param message    供货摘要
     * @param entity     返回信息字符串
     * @return FIRST_MODEL_KEY: 订单状态
     * SECOND_MODEL_KEY: 供货摘要
     * THIRD_MODEL_KEY: 上游返回信息字符串
     */
    private ResultDO returnFail(boolean success, int code, String logMessage, String message, String entity) {
        ResultDO result = new ResultSupport();
        result.setSuccess(success);
        result.setModel(ResultDO.FIRST_MODEL_KEY, code);
        result.setModel(ResultDO.SECOND_MODEL_KEY, message);
        result.setModel(ResultDO.THIRD_MODEL_KEY, entity);
        logger.debug(logMessage);
        return result;
    }
}
